


import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;

public class Analyzer {

    static ArrayList<Instruction> instructs = new ArrayList<Instruction>();
    static ArrayList<Instruction> new_instructs = new ArrayList<Instruction>();
	static int nameDependencyCount = 0;
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException
    {
    	
    	if (args.length == 0){
    		System.out.println("Incorrect number of arguments");
    		return;
    	}
    	
        //instructs = new ArrayList<Instruction>();
    	
        //Read a list of instrcutions from a file      
        File inputFile;
        String inpFile;
        //
        if (args[0].equalsIgnoreCase("-O")){
        	//Read second argument as the input file 	
        	inputFile = new File(args[1]);
        	inpFile = args[1];
        }
        else{
        	//Read first argument as the input file
        	inputFile = new File(args[0]); 
        	inpFile = args[0];
        }
        
        Global.cInstruct = new Instruction();
        Global.labels = new ArrayList<String>();
        
        
        Scanner scanner = new Scanner(new FileInputStream(inputFile));			//Open MIPS source for reading
        BufferedWriter out = new BufferedWriter(new FileWriter("out.txt"));		//Open text file to output instruction info
        String line = "";
        while(scanner.hasNextLine())
        {
            line = scanner.nextLine();

            //Create new instruction and parse the line.
            Instruction instr = new Instruction(line);			
            
            if(instr.name != "!")
            {
                //Print instruction info to file buffer
                out.write(instr.getInfo(line)); out.write("\n");	
                //Print instruction info to standard output
            	//System.out.println(instr.getInfo(line));			
            
                //Add the current instruction to the ArrayList
            	instructs.add(instr);	
            	
            }
            
        }
        
        //data dependencies
        for(int i = 0; i < instructs.size(); i++)
        {
            Instruction iInstruct = instructs.get(i);

            Dependency outputDep = null;
            HashMap<String, Dependency> antiDeps = 
                    new HashMap<String, Dependency>();
            //ArrayList<Dependency> trueDataDeps = 
                    //new ArrayList<Dependency>();
        	
        	
            for(int j = i + 1; j < instructs.size(); j++)
            {

                Instruction jInstruct = instructs.get(j);
                /*
				//if it's a jump, break control dependencies
                if(jInstruct.name.equals("J")
                        || jInstruct.name.equals("JAL")
                        || jInstruct.name.equals("JR"))
                {
                    Global.trackingControl = false;
                    Global.cInstruct = null;
                }
                
                //begin control dependencies for branch
                if(jInstruct.label && Global.labels.contains(jInstruct.name))
                {
                    Global.trackingControl = true;
                }
                    
                //add dependency if is part of a control loop
                if(Global.trackingControl = true && !jInstruct.label)
                {
                    Dependency control = new Dependency("Control", iInstruct,
                           jInstruct.Written);
                    if(!jInstruct.dependencies.contains(control))
                        jInstruct.dependencies.add(control);
                }
                
                //check for control dependencies
                if(jInstruct.Control)
                {
                    Global.cInstruct = jInstruct;
                    Global.labels.add(jInstruct.branch);
                }
                
                */
                //Check for True Data Dependencies
                //
                //
				if(iInstruct.Written != "")
                {
                    if(jInstruct.reads(iInstruct.Written) 
                            && outputDep == null)
                    {
                        //true data dependence!
                        jInstruct.dependencies.add(new Dependency("True Data", 
                                iInstruct, iInstruct.Written) );
                                
                    }

                    if(jInstruct.Written.equals(iInstruct.Written))
                    {
                        //output dependency!
                        outputDep = new Dependency("Output", iInstruct, 
                                iInstruct.Written);
                        jInstruct.dependencies.add(outputDep);
 						nameDependencyCount++;                       
                    }
                }
                
                //Zach's Edit
                //	Check to make sure a register was actually written.
                //	No longer recieve false positives for antidependencies.
                boolean jWrites = jInstruct.writes();
                if( jWrites /*&& /*!jInstruct.Written.equals("") &&*/ 
                        && jInstruct.Written.equals(iInstruct.Read1) 
                        && !antiDeps.keySet().contains(iInstruct.Read1))
                {
                    Dependency temp = new Dependency("Anti", iInstruct, 
                                iInstruct.Read1);
                    antiDeps.put(iInstruct.Read1, temp);
                    jInstruct.dependencies.add(temp);
                    nameDependencyCount++;
                }
                if( jWrites /*&& /*!jInstruct.Written.equals("") &&*/ 
                        && jInstruct.Written.equals(iInstruct.Read2) 
                        && !antiDeps.keySet().contains(iInstruct.Read2))
                {
                    Dependency temp = new Dependency("Anti", iInstruct, 
                            iInstruct.Read2);
                    antiDeps.put(iInstruct.Read2, temp);
                    jInstruct.dependencies.add(temp);
                    nameDependencyCount++;
                }
            }
        	
        }
        
        
        scanner.close();
		out.close();
		
		
/* //begin Zach's control logic
                
		Global.labels = new ArrayList<String>();
		Global.cInstructs = new ArrayList<Boolean>();
      	//Catalog labels      	
      	for (int i = 0; i < instructs.size();i++){
      		Instruction iInstruct = instructs.get(i);
      		if (iInstruct.label){
      			Global.labels.add(iInstruct.name);
      			Global.cInstructs.add(new Boolean(false));
      		}
      	}
      	//Mark branch labels
      	for (int i = 0; i < instructs.size();i++){
      		Instruction iInstruct = instructs.get(i);
      		if (iInstruct.name.equals("J") || iInstruct.name.equals("JAL") ||iInstruct.name.equals("JR")){
      			int labelNum = Global.labels.indexOf(iInstruct.Read2);
      			if (labelNum > -1) Global.cInstructs.set(labelNum, true);
      		}
      	}
// End Zach's control logic */        
        
        //steps for control dependencies:
                //1:  find control instruction
                //2:  determine label to which it branches
                //3:  find label and subsequent instructions
                //4:  break control dependency at a jump or branch
        for(int i = 0; i < instructs.size(); i++)
        {
            String label;
            Instruction findControl = instructs.get(i);
            if(findControl.Control)
            {
                //found a control, look for a label
                label = findControl.branch + ":";
                for(int j = 0; j < instructs.size(); j++)
                {
                    Instruction findLabel = instructs.get(j);
                    if(findLabel.label)
                    {
                        //found a label, see if it's the right one
                    	String temp = findLabel.name;
                    	//System.out.println(temp.equalsIgnoreCase(label));
                        if(temp.equalsIgnoreCase(label))
                        {
                            //label is matched, begin setting dependencies
                            //j+1 is the first instruction after the label
                            SetControlDependencies(findControl, j+1);
                        }
                    }
                }
            }
        }
        
        
        //************************************************
        //print deps to console
        //DisplayDependencies();
        
        
        //if optimizer flag set...
        if(args[0].equalsIgnoreCase("-o"))
        {
            //Begin Optimizing code.
            out = new BufferedWriter(new FileWriter(inpFile.subSequence(0,inpFile.length() - 2) + "--opt.s"));
        
            System.out.println("\nOptimizing...\n");
            register_map rMap = new register_map();
            for (int inst = 0; inst < instructs.size();inst++)
            {
                //Get current Instruction
                Instruction iInstruct = instructs.get(inst);			
                System.out.print(iInstruct + "\t\t--->\t\t");
                String wrt, r1, r2;
                //Get Write Register
                wrt = rMap.alias(iInstruct.Written);								
                //Get Read Register 1. Substitute an alias if it has been renamed.
                r1 = rMap.alias(iInstruct.Read1);						
                //Get Read Register 2. Substitute an alias if it has been renamed.
                r2 = rMap.alias(iInstruct.Read2);

                
                rMap.setUsed(wrt);
                
                //Check if a control dependency exists due to branch
				/*
				if (iInstruct.label){
                    int labelNum = Global.labels.indexOf(iInstruct.name);
    				if ( Global.cInstructs.get(labelNum).equals( Boolean.valueOf(true) ) ){
                            rMap.reset();
                            System.out.println("# Encountered subroutine. Dumping register map to reflect control dependence.");
                    }
                }
				*/
                for (int dep = 0; dep < iInstruct.dependencies.size(); dep++)
                {   
                    Dependency dependency = iInstruct.dependencies.get(dep);

                    //if the dependency list contains anything but name, we can't
                    //optimize
                    if(!dependency.myType.equalsIgnoreCase("control")
                            && !dependency.myType.equalsIgnoreCase("True Data"))
                    {

                        if (dependency.myType.equalsIgnoreCase("Anti") 
                                || dependency.myType.equalsIgnoreCase("Output"))
                        {
                            //Handle anti-dependence by renaming the write register.
                            //	OR
                            //Handle Output dependence by renaming the write 
                            //register of the second write command.
                            
                            if ( rMap.alias(dependency.register).equals(wrt) ){ 
                                // If Write register is not renamed, rename it.
                                if (rMap.alias(wrt).equals(wrt))
                                { 
                                    wrt = rMap.rename(wrt);
                                }			
                                else 
                                {																// Otherwise, find its name
                                    wrt = rMap.alias(wrt);							
                                }
                            }
                            
                        }
                        else{
                                //True Data Dependence: Do Nothing
                                //Control Dependence: Do Nothing
                        }
                    }
                }
            	//Change the instruction
            	iInstruct.Written = wrt;
            	iInstruct.Read1 = r1;
            	iInstruct.Read2 = r2;
            	
            	System.out.println(iInstruct);
            	new_instructs.add(iInstruct);
        		out.write(iInstruct + "\n");
            }
			out.close();                      	
        }        
           
        GraphViz gv = new GraphViz(instructs, inpFile);
        //System.out.println(gv.ToString());
        gv.Write();
        System.out.println("Name dependencies in input file: " + nameDependencyCount);
 
    }
    
    
    public static void SetControlDependencies(Instruction control, int index)
    {
        //we have found a label and wish to inspect the sequential instructions
        //if the instruction is not a branch of some sort, add a control
        //dependency to the array list
        Instruction inspect;
        boolean done = false;
        
        while(!done)
        {
            inspect = instructs.get(index);
            String type = inspect.name;
            
            if(type.equalsIgnoreCase("j") || type.equalsIgnoreCase("jr")
                    || type.equalsIgnoreCase("jal") || type.equalsIgnoreCase("ret"))
                //branch encountered, no more dependencies in this sequence
                done = true;
            else
            {
                //it passed the tests, it is not a branch
                //add it to the list of dependencies
                Dependency dep = new Dependency("Control", control);
                inspect.dependencies.add(dep);
                index++;
            }
        }
    }
    
    public static void DisplayDependencies()
    {
    //Print Dependencies
        //System.out.println("\nDependencies Listed\n");
        for (int i =0; i < instructs.size(); i++){
            Instruction iInstruct = instructs.get(i);
            System.out.println(iInstruct);
            for (int j = 0; j < iInstruct.dependencies.size(); j++){
                Dependency jDependency = iInstruct.dependencies.get(j);
                System.out.println("\t" + jDependency);
            }
        }
    }
}
